// Copyright 2022 Huawei Cloud Computing Technology Co., Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

extern "C" {}

#include "CaeCommon.h"

#include <cstdio>
#include <vector>
#include <string>
#include <cctype>
#include <cerrno>
#include <fstream>
#include <sstream>

#include "../hwsecure/securec.h"
#include "CaeLogConfig.h"

using namespace std;

CaeCommon::CaeCommon() = default;

CaeCommon::~CaeCommon() = default;

void CaeCommon::PrintfCurrentTime(const string &logInfo)
{
    struct timeval curTime {};
    tm times {};
    gettimeofday(&curTime, nullptr);
    tm *pTm = gmtime(&(curTime.tv_sec));
    if (nullptr != pTm) {
        times = *pTm;
        long long diffUsec = curTime.tv_usec;
        LOGI("%s, time:%d-%02d-%02d %02d:%02d:%02d:%lld.", logInfo.c_str(), times.tm_year + 1900,
            times.tm_mon + 1, times.tm_mday, times.tm_hour, times.tm_min, times.tm_sec, diffUsec / 1000);
    } else {
        LOGE("PTm is null");
    }
};

time_t CaeCommon::GetCurrentTimestamp()
{
    struct timeval curTime {};
    gettimeofday(&curTime, nullptr);
    long milliseconds = curTime.tv_sec * 1000LL + curTime.tv_usec / 1000;
    return milliseconds;
}

string CaeCommon::ToUTCTime(time_t mTime)
{
    if (mTime == 0) {
        return "invalid time";
    }
    tm *pTm = gmtime(&mTime);
    if (nullptr == pTm) {
        return "invalid time";
    }
    tm tmTime = *pTm;
    string mUtcTime;
    char *timeStr = new (std::nothrow) char[CHAR_SIZE];
    if (nullptr == timeStr) {
        LOGE("TimeStr is null");
        return "invalid time";
    }
    int ret = memset_s(timeStr, CHAR_SIZE, 0, CHAR_SIZE);
    if (ret != EOK) {
        LOGE("Failed to memset timeStr.");
        delete[] timeStr;
        timeStr = nullptr;
        return "invalid time";
    }
    ret = snprintf_s(timeStr, CHAR_SIZE, CHAR_SIZE - 1, "%d-%02d-%02dT%02d:%02d:%02dZ", tmTime.tm_year + 1900,
        tmTime.tm_mon + 1, tmTime.tm_mday, tmTime.tm_hour, tmTime.tm_min, tmTime.tm_sec);
    if (ret == -1) {
        LOGE("Failed to snprintf_s timeStr.");
        delete[] timeStr;
        timeStr = nullptr;
        return "invalid time";
    }
    mUtcTime = (const char *)timeStr;
    delete[] timeStr;
    timeStr = nullptr;
    return mUtcTime;
};

string CaeCommon::To24Hour(time_t mTime)
{
    tm *pTm = gmtime(&mTime);
    if (nullptr == pTm) {
        LOGE("Failed to gmtime.");
        return "invalid time";
    }
    tm tmTime = *pTm;
    string to24Hour;
    char *timeStr = new (std::nothrow) char[CHAR_SIZE];
    if (nullptr == timeStr) {
        LOGE("timeStr is nullptr");
        return "invalid time";
    }
    int ret = snprintf_s(timeStr, CHAR_SIZE, CHAR_SIZE - 1, "%d-%02d-%02d %02d:%02d:%02d", tmTime.tm_year + 1900,
        tmTime.tm_mon + 1, tmTime.tm_mday, tmTime.tm_hour, tmTime.tm_min, tmTime.tm_sec);
    if (ret == -1) {
        LOGE("Failed to snprintf_s timeStr.");
        return "invalid time";
    }
    to24Hour = (const char *)timeStr;
    delete[] timeStr;
    timeStr = nullptr;
    return to24Hour;
};

std::string CaeCommon::ToLowerCaseStr(std::string str)
{
    for (char &i : str) {
        i = static_cast<char>(tolower(i));
    }
    return str;
}

std::string CaeCommon::ToISO8601Time(time_t &time)
{
    char buffer[32];
    tm *pTm = gmtime(&time);
    if (nullptr == pTm) {
        LOGE("pTm is null.");
        return "";
    }
    strftime(buffer, sizeof(buffer), "%Y%m%dT%H%M%SZ", pTm);
    std::string str(buffer);
    return str;
}

uint32_t CaeCommon::StrToUInt32(const char *key, const char *value, uint32_t defaultValue)
{
    int base = 10;
    char *endPtr = nullptr;
    const char *strPtr = value;
    long val = strtol(strPtr, &endPtr, base);
    if (errno == ERANGE && (val == LONG_MAX || val == LONG_MIN)) {
        LOGW("Failed to convert key %s, value %s, default value %d.", key, value, defaultValue);
        return defaultValue;
    }

    if (endPtr == strPtr) {
        LOGW("Digits not found, key %s, value %s, default value %d.", key, value, defaultValue);
        return defaultValue;
    }

    if (val >= UINT32_MAX || val < 0) {
        LOGW("Out of range, key %s, value %s, default value %d.", key, value, defaultValue);
        return defaultValue;
    }

    return (uint32_t)val;
}


bool CaeCommon::IsFileExist(const std::string& name)
{
    if (FILE *file = fopen(name.c_str(), "r")) {
        fclose(file);
        LOGI("file %s is exist", name.c_str());
        return true;
    } else {
        LOGE("file %s is not exist", name.c_str());
        return false;
    }
}

std::string CaeCommon::ReadFileToString(const std::string& fileName)
{
    std::ifstream file(fileName);
    std::ostringstream temp;
    temp << file.rdbuf();
    return temp.str();
}

int CaeCommon:: CompareVersion(const string& version1, const string& version2)
{
    uint32_t i = 0, j = 0;
    uint32_t n1 = version1.size(), n2 = version2.size();
    uint32_t num1 = 0, num2 = 0;
    bool isBVersion1 = false, isBVersion2 = false;
    while (i < n1 || j < n2) {
        while (i < n1 && version1[i] != '.') {
            if (version1[i] == 'B') {
                isBVersion1 = true;
            }
            num1 = num1 * 10 + (version1[i] - '0');
            i++;
        }
        while (j < n2 && version2[j] != '.') {
            if (version2[j] == 'B') {
                isBVersion2 = true;
            }
            num2 = num2 * 10 + (version2[j] - '0');
            j++;
        }
        if (isBVersion1 && num2 == 0) {
            return -1;
        } else if (isBVersion2 && num1 == 0) {
            return 1;
        }

        if (isBVersion1 && !isBVersion2) {
            num1 = 0;
        }
        if (isBVersion2 && !isBVersion1) {
            num2 = 0;
        }

        if (num1 > num2) {
            return 1;
        } else if (num1 < num2) {
            return -1;
        }
        num1 = 0;
        num2 = 0;
        i++;
        j++;
    }
    return 0;
}